const dazi = function (
	dom,
	{
		text = null, // 输出的文字(string or array)
		showCursor = true, // 是否展示光标
		cursorFlickerTime = 1000, // 光标闪烁周期时间
		backtrack = true, // 是否回溯
		speed = 100, // 打字速度
		pauseTime = 3000, // 停顿时间
		deleteSpeed = 30, // 文字删除速度
		mode = 'cycle', // 输出模式 cycle or random or once
		once_backtrack = true, // 在单次输出模式下，最后一次输出是否回溯
		once_showCursor = true, // 在单次输出模式下，最后一次输出完后光标是否展示
		random_first = null, // 在随机输出模式下，第一次输出的内容
	} = {}
) {
	this.dom = dom;
	this.doms = null;
	this.options = {
		text,
		showCursor,
		cursorFlickerTime,
		backtrack,
		speed,
		pauseTime,
		deleteSpeed,
		mode,
		once_backtrack,
		once_showCursor,
		random_first,
	};
	this.state = {
		text: '',
		textNodeArr: [],
		index: null,
		textIndex: null,
		model: 'add', // 当前文字模式 add or del
		cursorState: false, // 光标当前状态
		uninstall: false, // 是否卸载
		pause: false, // 是否暂停
	};

	this.init();
};

// 方法需要使用的数据
dazi.prototype._data = {
	cursor_changLiang: false,
	indexBeforePause: null,
	textPause_BeforePause: false, // 暂停前文字是否处于停顿状态
	previousIndex: null, // 上一个索引
	random_first: null, // 当 random_first 被设置了值时，用数字表示设置值的状态，1：数字，2：字符串
	cursor_timer: null,
	write_timer: null,
};

// 参数检查方法对象
dazi.prototype.check = {
	text(s) {
		return typeof s === 'string' || Array.isArray(s);
	},
	dom(s) {
		return s instanceof HTMLElement;
	},
};

// 初始化方法
dazi.prototype.init = function () {
	// 验证dom
	if (!this.check.dom(this.dom)) throw new Error('第一个参数不是 HTML元素');

	// 验证配置项
	if (!this.check.text(this.options.text)) throw new Error('配置项错误："text"');

	// 格式化输出文字
	if (Array.isArray(this.options.text)) {
		this.options.text = this.options.text.map((i) => i.toString());
	} else {
		this.options.text = [this.options.text];
	}

	// 初始化状态
	this.state.text = this.options.text[0];
	if (this.options.mode === 'random') {
		switch (typeof this.options.random_first) {
			case 'string':
				if (this.options.random_first) {
					this._data.random_first === 1;
					this.options.text.unshift(this.options.random_first);
					this.state.index = 0;
				} else {
					this.state.index = this.randomIndex();
				}
				break;
			case 'number':
				if (this.options.text[this.options.random_first] && Array.isArray(this.options.text)) {
					this._data.random_first === 2;
					this.state.index = this.options.random_first;
					this._data.previousIndex = this.options.random_first;
				} else {
					this.state.index = this.randomIndex();
				}
				break;
		}
	} else {
		this.state.index = 0;
	}
	this.state.textIndex = 0;

	const textDom = document.createElement('span');
	const cursorDom = document.createElement('span');

	cursorDom.innerText = '|';
	cursorDom.style.cssText = `opacity: 0; vertical-align: baseline; user-select: none; transform: scale(1, 1.215) translate(0, -8.8%); display: none;`;

	this.dom.appendChild(textDom);
	this.dom.appendChild(cursorDom);

	this.doms = { textDom, cursorDom };

	this.cursor_init();

	this.write();
};

// 文字展示方法
dazi.prototype.textShow = function () {
	const textNode = this.state.textNodeArr[this.state.textIndex];
	if (this.state.model === 'add') {
		this.doms.textDom.appendChild(textNode);
	} else {
		this.doms.textDom.removeChild(textNode);
	}
};

// 写方法
dazi.prototype.write = function (tc) {
	const thia = this;

	const text = this.options.text[this.state.index];
	let speed = this.options.speed;
	// 创建当前输出字符串的文本节点数组
	if (!tc || this._data.indexBeforePause !== this.state.index) {
		this.state.textNodeArr = text.split('').map((i) => document.createTextNode(i));
	}

	function end() {
		function init() {
			thia.state.textIndex = 0;
			thia.doms.textDom.innerHTML = '';
		}

		clearTimeout(thia._data.write_timer);
		thia.cursor_changLiang = false;

		switch (thia.options.mode) {
			case 'cycle':
				if (thia.state.index >= thia.options.text.length - 1) thia.state.index = 0;
				thia.state.index++;
				init();
				thia.write();
				break;

			case 'random':
				if (thia._data.random_first) {
					if (thia._data.random_first === 1) thia.options.text.shift();
					this.options.random_first = null;
				}
				thia.state.index = thia.randomIndex();
				init();
				thia.write();
				break;

			case 'once':
				if (thia.state.index < thia.options.text.length - 1) {
					thia.state.index++;
					init();
					thia.write();
				} else {
					thia.cursor_show(false);
				}
		}
	}

	function pause(s) {
		thia.cursor_changLiang = false;
		clearTimeout(thia._data.write_timer);
		setTimeout(() => {
			if (thia.state.model === 'add') {
				end();
			} else {
				thia.cursor_changLiang = true;
				speed = thia.options.deleteSpeed;
				cycle(true);
			}
		}, thia.options.pauseTime);
	}

	thia.cursor_changLiang = true;
	function cycle(s) {
		if (thia.state.pause) return;

		thia.state.text = text.slice(0, thia.state.textIndex + 1);

		thia.textShow();

		if (thia.state.textIndex >= text.length - 1) {
			if (
				thia.options.mode === 'once' &&
				!thia.options.once_backtrack &&
				thia.state.index === thia.options.text.length - 1
			) {
				return end();
			} else if (thia.options.backtrack) {
				thia.state.model = 'del';
				if (thia._data.textPause_BeforePause) {
					thia._data.textPause_BeforePause = false;
				} else {
					if (!s) return pause();
				}
			} else {
				return pause();
			}
		} else if (thia.state.textIndex <= 0 && thia.state.model === 'del') {
			thia.state.model = 'add';
			return end();
		}

		if (thia.state.model === 'add') {
			thia.state.textIndex++;
		} else {
			thia.state.textIndex--;
		}

		thia._data.write_timer = setTimeout(() => {
			cycle();
		}, speed);
	}

	cycle();
};

dazi.prototype.randomIndex = function () {
	let s = ~~(Math.random() * this.options.text.length);

	if (this.options.text.length > 1) {
		while (s === this._data.previousIndex) {
			s = ~~(Math.random() * this.options.text.length);
		}

		this._data.previousIndex = s;
	}

	return s;
};

// 光标方法
dazi.prototype.cursor_init = function () {
	if (this.options.showCursor) {
		this.cursor_show(true);

		this.cursor_flicker();

		delete dazi.prototype.cursor_changLiang;
		Object.defineProperty(dazi.prototype, 'cursor_changLiang', {
			configurable: true,
			get() {
				return this._data.cursor_changLiang;
			},
			set(s) {
				if (s) {
					this.doms.cursorDom.style.opacity = '1';
					this.state.cursorState = true;
					clearInterval(this._data.cursor_timer);
					this._data.cursor_timer = null;
				} else {
					this.cursor_flicker();
				}

				this._data.cursor_changLiang = s;
			},
		});
	}
};
dazi.prototype.cursor_flicker = function () {
	if (this._data.cursor_timer) return;
	this._data.cursor_timer = setInterval(() => {
		if (this.state.pause) {
			clearInterval(this._data.cursor_timer);
			return;
		}
		this.state.cursorState = !this.state.cursorState;
		this.doms.cursorDom.style.opacity = Number(this.state.cursorState);
	}, this.options.cursorFlickerTime / 2);
};
dazi.prototype.cursor_show = function (s) {
	let a;
	if (s) {
		a = 'inline-block';
	} else {
		a = 'none';
		clearInterval(this._data.cursor_timer);
		this._data.cursor_timer = null;
	}
	this.doms.cursorDom.style.display = a;
};

// 暂停方法
dazi.prototype.pause = function () {
	this.state.pause = !this.state.pause;

	if (!this.state.pause) {
		this.write(true);
		this.cursor_flicker();
	} else {
		this._data.indexBeforePause = this.state.index;
		this._data.textPause_BeforePause = true;
	}
};

// 卸载方法
dazi.prototype.uninstall = function () {
	clearInterval(this._data.cursor_timer);
	clearTimeout(this._data.write_timer);

	this.dom.removeChild(this.doms.cursorDom);
	this.dom.removeChild(this.doms.textDom);

	this.doms.cursorDom = null;
	this.doms.textDom = null;

	this.state.uninstall = true;
};
